using System;
using System.Collections.Generic;
using UnityEngine;

namespace RedPoint.Runtime {
    public class RedPointManager
    {
        public const string CharSplit = "/";

        private static RedPointManager instance;

        public static RedPointManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new RedPointManager();
                }
                return instance;
            }
        }
        
        // 根节点
        public PointNode Root { get; private set; }
        // 所有节点（节点路径，节点引用）
        private Dictionary<string, PointNode> m_AllNodes;
        // 脏节点
        private HashSet<PointNode> m_DirtyNodes;
        // 临时脏节点列表
        private List<PointNode> m_TempDirtyNodes;

        /// <summary>
        /// 节点数量改变回调
        /// </summary>
        public Action NodeNumChangeCallback;
        /// <summary>
        /// 节点值改变回调
        /// </summary>
        public Action<PointNode, int> NodeValueChangeCallback;

        /// <summary>
        /// 注册节点并绑定监听
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public PointNode AddListener(string path, Action<int> callback)
        {
            PointNode node;
            if (!GetNode(path, out node)) // 不存在节点时先创建
            {
                node = AddNode(path);
            }
            
            node.AddListenerChangeCallback(callback);
            
            return node;
        }
        
        /// <summary>
        /// 移除节点监听
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public bool RemoveListener(string path, Action<int> callback)
        {
            PointNode node;
            if (GetNode(path, out node))
            {
                node.RemoveListenerChangeCallback(callback);
                return true;
            }

            return false;
        }

        /// <summary>
        /// 移除节点的所有监听
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public bool RemoveAllListeners(string path)
        {
            PointNode node;
            if (GetNode(path, out node))
            {
                node.RemoveAllListenersChangeCallback();
                return true;
            }

            return false;
        }

        /// <summary>
        /// 添加节点
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private PointNode AddNode(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                Debug.LogError($"路径字符串为空");
                return null;
            }

            if (m_AllNodes.ContainsKey(path))
            {
                Debug.LogError("路径重复添加");
                return null;
            }
            
            // 从根节点开始创建
            PointNode cur = Root;
            
            string[] paths = path.Split(CharSplit);
            foreach (var name in paths)
            {
                // 自动获取子节点 || 不存在时自动添加子节点
                PointNode child = cur.GetOrAddChild(name);
                cur = child;
            }
            
            m_AllNodes.Add(path, cur);
            
            return cur;
        }

        /// <summary>
        /// 获取节点
        /// </summary>
        /// <param name="path"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool GetNode(string path, out  PointNode node)
        {
            node = null;
            if (string.IsNullOrEmpty(path))
            {
                Debug.LogError($"路径字符串为空");
                return false;
            }
            
            return m_AllNodes.TryGetValue(path, out node);
        }

        /// <summary>
        /// 移除节点
        /// </summary>
        /// <param name="path"></param>
        public void RemoveNode(string path)
        {
            if (GetNode(path, out PointNode node))
            {
                // 查找节点，通过节点的Name在父节点中删除
                node.Parent.RemoveChild(node.Name);
                m_AllNodes.Remove(path);
            }
        }
        
        /// <summary>
        /// 移除所有节点
        /// </summary>
        public void RemoveAllTreeNode()
        {
            Root.RemoveAllChild();
            m_AllNodes.Clear();
        }

        /// <summary>
        /// 改变节点值
        /// </summary>
        /// <param name="path"></param>
        /// <param name="value"></param>
        public void ChangeValue(string path, int value)
        {
            if (!GetNode(path, out PointNode node))
            {
                Debug.LogError("节点不存在，无法改变数值");
                return;
            }
            
            node.ChangeValue(value);
        }

        public int GetNodeValue(string path)
        {
            if (GetNode(path, out PointNode node))
            {
                return node.Value;
            }

            return 0;
        }

        /// <summary>
        /// 标记节点为脏节点
        /// </summary>
        /// <param name="node"></param>
        public void MarkDirtyNode(PointNode node)
        {
            if (node == null || node == Root)
            {
                return;
            }

            m_DirtyNodes.Add(node);
        }
        
        private RedPointManager()
        {
            m_AllNodes = new Dictionary<string, PointNode>();
            Root = new PointNode("Root");
            m_DirtyNodes = new HashSet<PointNode>();
            m_TempDirtyNodes =  new List<PointNode>();
        }

        public void Update()
        {
            if (m_DirtyNodes.Count == 0)
            {
                return;
            }
            
            // 脏节点移动到临时脏列表
            m_TempDirtyNodes.Clear();
            foreach (var node in m_DirtyNodes)
            {
               m_TempDirtyNodes.Add(node); 
            }
            m_DirtyNodes.Clear();
            
            // 进行更新
            foreach (var tempNode in m_TempDirtyNodes)
            {
                tempNode.ChangeValue();
            }
        }
    }
    
}

